﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Web;
using System.Web.Script.Serialization;
using System.Web.UI;
using System.Web.UI.WebControls;
using AjaxControlToolkit.LongOperations;
using Image = System.Web.UI.WebControls.Image;
using ThreadPool = AjaxControlToolkit.LongOperations.ThreadPool;


[assembly: WebResource("ControlToolkitExtended.LongOperations.loading2.gif", "image/gif")]
[assembly: WebResource("ControlToolkitExtended.LongOperations.UpdatePanelExtenderStyle.css", "text/css", PerformSubstitution = true)]
[assembly: WebResource("ControlToolkitExtended.LongOperations.UpdatePanelExtenderBehavior.js", "text/javascript")]

namespace AjaxControlToolkit
{
    /// <summary>
    /// Этот делегад является делегатом для запуска длинной операции в отдельном потоке
    /// </summary>
    /// <returns>Serializable object with additional data to refresh page content</returns>
    public delegate IOperationExecutor GetExecutorDelegate();
    /// <summary>
    /// Делегат, который определяет обратные функции вызова после успешного или ошибочного завершения задачи
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void UpdatePanelExtenderEventHandler(object sender, UpdatePanelExtenderEventArgs e);

    public enum ExecutionResultState
    {
        Undefined = 0,
        Executing = 1,
        Successed = 2,
        Error = 3,
        Canceled = 4,
        Started = 5,
        NotFound = 6
    }

    [Designer("ControlToolkitExtended.UpdatePanelExtender, AjaxControlToolkit")]
    [RequiredScript(typeof(CommonToolkitScripts), 0)]
    [ClientScriptResource("AjaxControlToolkit.UpdatePanelExtender", "ControlToolkitExtended.LongOperations.UpdatePanelExtenderBehavior.js", LoadOrder = 1)]
    [ClientCssResource("ControlToolkitExtended.LongOperations.UpdatePanelExtenderStyle.css")]
    [TargetControlType(typeof(UpdatePanel))]
    public class UpdatePanelExtender : ExtenderControlBase
    {
        #region Constantes
        private static class Constants
        {
            public static readonly string cmd_GetState = "upegs";
            public static readonly string cmd_CancelExecution = "upece";
            public static readonly string cmd_StartExecution = "upse";
            public static readonly string UpdatePanelExtenderProcessGUIDKey = "UpdatePanelExtenderPorcessGUID";
            public static readonly string UpdatePanelExtenderCommandKey = "UpdatePanelExtenderCommand";
            public static readonly string UpdatePanelExtenderElementID = "elemid";
        }
        #endregion Constantes

        #region WorkReuestWrapper

        private class WorkReuestWrapper: WorkRequest
        {
            internal IOperationExecutor _executor;
            IOperationResultSynchronizer _synchronizer;
            private string _sessionID = Guid.Empty.ToString();

            public WorkReuestWrapper(IOperationExecutor executor, IOperationResultSynchronizer synchronizer, string sessionID)
                : base((Delegate)null, null, true, true, true, true)
            {
                _executor = executor;
                _synchronizer = synchronizer;
                DelegateWrapperDelegate proc = DelegateWrapper;
                targetProc = proc;
                procArgs = new object[1]{null};
                procArg = null;
                _sessionID = sessionID;
            }

            delegate void DelegateWrapperDelegate(object  state);

            private void DelegateWrapper(object  state)
            {
                UpdatePanelExtenderEventArgs eventArgs = new UpdatePanelExtenderEventArgs("", DateTime.Now, ExecutionResultState.Executing);
                _synchronizer.SetOperationState(_sessionID, WorkRequestID.ToString(), eventArgs);
				string result = "";
                try
                {
                    result = this._executor.Execute();
                    eventArgs = _synchronizer.GetOperationState(_sessionID, WorkRequestID.ToString());
                    eventArgs = new UpdatePanelExtenderEventArgs(result, eventArgs.StartExecutionTime, ExecutionResultState.Successed);
                }
                catch(ThreadAbortException e)
                {
                    eventArgs = _synchronizer.GetOperationState(_sessionID, WorkRequestID.ToString());
                    eventArgs = new UpdatePanelExtenderEventArgs("", eventArgs.StartExecutionTime, ExecutionResultState.Canceled);
                }
                catch (Exception e)
                {
                    eventArgs = _synchronizer.GetOperationState(_sessionID, WorkRequestID.ToString());
                    eventArgs = new UpdatePanelExtenderEventArgs(e.Message, eventArgs.StartExecutionTime, ExecutionResultState.Error);
                }
                _synchronizer.SetOperationState(_sessionID, WorkRequestID.ToString(), eventArgs);
            }
        }

        #endregion WorkReuestWrapper

        private class ClientStateValue
        {
            public string processGUID;
        }

        private static readonly ThreadPool _threadPool;
        private static readonly object _eventComplete = new object();
        private static readonly object _eventFailed = new object();
        private static readonly JavaScriptSerializer _serializer = new JavaScriptSerializer();
        private bool _isExecutionWasStarted;
        private string _startedProcessGUID = "";
        private UpdatePanel _updatePanel;
        private ModalPopupExtender _modalPopup = null;
        private bool _serverMethodCalledWithFinishedState;
        private Panel _popupPanel = null;
        private Button _cancelButton = null;
        private Label _statusLabel = null;
        private Image _customTrobber = null;
        //private EventSuppressor<HttpApplication> _httpApplicationEventSuppressor = null;
        private IOperationResultSynchronizer _synchronizer = null;

        static UpdatePanelExtender()
        {
            _threadPool =  new ThreadPool(5, 20, "UPDATE_PANEL_THREADPOOL");
            _threadPool.Start();
        }

        public UpdatePanelExtender()
        {
            _synchronizer = LongOperationResultManager.Instance;
            EnableClientState = true;
            ClientStateValuesLoaded += UpdatePanelExtender_ClientStateValuesLoaded;
            //try to send answer as fast as possible
            if (HttpContext.Current != null)
            {
                if (HttpContext.Current.Request.QueryString[Constants.UpdatePanelExtenderCommandKey] != null)
                {
                    string command = HttpContext.Current.Request.QueryString[Constants.UpdatePanelExtenderCommandKey];
                    //let throw Page initialization on process startup, current clientID is not right
                    if (command != Constants.cmd_StartExecution)
                        ResolveCommandResult(
                            command, 
                            HttpContext.Current.Request.QueryString[Constants.UpdatePanelExtenderProcessGUIDKey],
                            HttpContext.Current.Request.QueryString[Constants.UpdatePanelExtenderElementID]);
                }
            }
        }

        [Category("Behavior")]
        [Description("ID of ModalPopup")]
        [DefaultValue("")]
        [IDReferenceProperty]
        public string ModalpopupID
        {
            get
            {
                return (string)(ViewState["ModalpopupID"] ?? string.Empty);
            }
            set
            {
                ViewState["ModalpopupID"] = value;
            }
        }

        private string _getExecutorMethodName;

        /// <summary>
        /// Статический метод страницы, который должен выполнять длительную операцию
        /// </summary>
        [DefaultValue("")]
        public string GetExecutorMethod
        {
            get
            {
                return _getExecutorMethodName;
            }
            set
            {
                _getExecutorMethodName = value;
            }
        }

        [Category("Behavior")]
        [Description("Update status period in milliseconds")]
        [DefaultValue(1000)]
        [ClientPropertyName("updatePeriod")]
        public int UpdateStatusRequestFrequency
        {
            get
            {
                return GetPropertyValue<int>( "UpdateStatusRequestFrequency", 1000);
            }
            set
            {
                SetPropertyValue<int>("UpdateStatusRequestFrequency", value);
            }
        }

        /// <summary>
        /// Вызывается на стороне сервера после успешного завершения отработки
        /// </summary>
        public event UpdatePanelExtenderEventHandler ExecutionComplete
        {
            add { Events.AddHandler(_eventComplete, value); }
            remove { Events.RemoveHandler(_eventComplete, value); }
        }

        protected void OnExecutionComplete(UpdatePanelExtenderEventArgs e)
        {
            UpdatePanelExtenderEventHandler handler = (UpdatePanelExtenderEventHandler) Events[_eventComplete];
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Вызывается на стороне сервера после неудачного завершения отработки
        /// </summary>
        public event UpdatePanelExtenderEventHandler ExecutionFailed
        {
            add { Events.AddHandler(_eventFailed, value); }
            remove { Events.RemoveHandler(_eventFailed, value); }
        }

        protected void OnExecutionFailed(UpdatePanelExtenderEventArgs e)
        {
            UpdatePanelExtenderEventHandler handler = (UpdatePanelExtenderEventHandler)Events[_eventFailed];
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected override void OnInit(EventArgs e)
        {
            if (Page.Request.QueryString[Constants.UpdatePanelExtenderCommandKey] != null)
            {
                ResolveCommandResult(Page.Request.QueryString[Constants.UpdatePanelExtenderCommandKey],
                                     Page.Request.QueryString[Constants.UpdatePanelExtenderProcessGUIDKey],
                                     ClientID);
            }
            else
            {
                base.OnInit(e);
                if(!_threadPool.IsStarted)
                {
                    _threadPool.Start();
                }
                Page.PreLoad += Page_PreLoad;
            }
        }

        void Page_PreLoad(object sender, EventArgs e)
        {
            PopulatePopup();
            if (!string.IsNullOrEmpty(_startedProcessGUID))
            {
                Page.PreLoad -= Page_PreLoad;
                ProcessServerMethods();
            }
        }


        void UpdatePanelExtender_ClientStateValuesLoaded(object sender, EventArgs e)
        {
            string clientState = ClientState;
            if (!string.IsNullOrEmpty(clientState))
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                ClientStateValue clientStateValue = serializer.Deserialize<ClientStateValue>(clientState);
                _startedProcessGUID = clientStateValue.processGUID;
                ProcessServerMethods();
            }
        }

        protected void ProcessServerMethods()
        {
            if (_serverMethodCalledWithFinishedState || _isExecutionWasStarted)
                return;
            UpdatePanelExtenderEventArgs eventArgs = _synchronizer.GetOperationState(Context.Session.SessionID, _startedProcessGUID);
            _synchronizer.ClearOperationStates(Context.Session.SessionID);
            if (eventArgs.ExecutionResultState != ExecutionResultState.NotFound)
            {
                //clearing all event handlers to send error to client
//                if ((eventArgs.ExecutionResultState == ExecutionResultState.Error) ||
//                    (eventArgs.ExecutionResultState == ExecutionResultState.Successed))
//                {
//                    _httpApplicationEventSuppressor = new EventSuppressor<HttpApplication>(Context.ApplicationInstance);
//                    _httpApplicationEventSuppressor.Suppress();
//                    Context.ApplicationInstance.Error += ApplicationInstance_Error;
//                    Context.ApplicationInstance.EndRequest += new EventHandler(ApplicationInstance_EndRequest);
//                }
                if (eventArgs.ExecutionResultState == ExecutionResultState.Successed)
                {
                    _serverMethodCalledWithFinishedState = true;
					_isExecutionWasStarted = false;
                    OnExecutionComplete(eventArgs);
                }
                else if (eventArgs.ExecutionResultState == ExecutionResultState.Error)
                {
                    _serverMethodCalledWithFinishedState = true;
					_isExecutionWasStarted = false;
                    OnExecutionFailed(eventArgs);
                }
            }
        }

//        void ApplicationInstance_EndRequest(object sender, EventArgs e)
//        {
//            if (_httpApplicationEventSuppressor != null)
//            {
//                _httpApplicationEventSuppressor.Resume();
//                _httpApplicationEventSuppressor = null;
//            }
//        }
//
//        void ApplicationInstance_Error(object sender, EventArgs e)
//        {
//            Exception exc = Context.Error;
//            UpdatePanelExtenderEventArgs eventArgs = new UpdatePanelExtenderEventArgs(exc == null ? "Unhandled exception when result handling" : exc.Message, DateTime.Now, ExecutionResultState.Canceled);
//            Context.ClearError();
//            if (_httpApplicationEventSuppressor != null)
//            {
//                _httpApplicationEventSuppressor.Resume();
//                _httpApplicationEventSuppressor = null;
//            }
//            PushResultToClint(eventArgs, ClientID);
//        }

        private void ResolveCommandResult(string command, string processGUID, string elementID)
        {
            UpdatePanelExtenderEventArgs eventArgs;
            if (!string.IsNullOrEmpty(processGUID))
            {
                HttpContext context = HttpContext.Current;
                eventArgs = _synchronizer.GetOperationState(context.Session.SessionID, processGUID);
                if (command == Constants.cmd_GetState)
                {
                    //получаем информацию о текущем состоянии задачи, сообщаем клиенту
                }
                else if (command == Constants.cmd_CancelExecution)
                {
                    //убиваем задачу, сообщаем клиенту
                    if (_threadPool.AbortWorkRequest(new Guid(processGUID) ))
                    {
                        eventArgs = new UpdatePanelExtenderEventArgs("Porcess aborted", eventArgs.StartExecutionTime, ExecutionResultState.Error);
                    }
                    else
                    {
                        eventArgs = new UpdatePanelExtenderEventArgs("Can't abort task", eventArgs.StartExecutionTime, eventArgs.ExecutionResultState);
                    }
                }
                else
                {
                    //сообщаем клиенту, что такая команда не поддерживается
                    eventArgs = new UpdatePanelExtenderEventArgs("Command is not supported", eventArgs.StartExecutionTime, ExecutionResultState.Error);
				}
                if ((eventArgs.ExecutionResultState == ExecutionResultState.Error) ||
                    (eventArgs.ExecutionResultState == ExecutionResultState.Canceled) ||
                    (eventArgs.ExecutionResultState == ExecutionResultState.NotFound))
                {
                    _synchronizer.ClearOperationStates(context.Session.SessionID);
                }
            }
            else if (command == Constants.cmd_StartExecution)
            {
                if (ReserveLongOperation())
                {
                    eventArgs = new UpdatePanelExtenderEventArgs(_startedProcessGUID, DateTime.Now, ExecutionResultState.Started);
                }
                else
                {
                    eventArgs = new UpdatePanelExtenderEventArgs("An error hase occured when starting process", DateTime.Now, ExecutionResultState.Error);
                }
            }
            else
            {
                //здесь необходимо отправить на клиент сообщение о том, что идентификатор процесса не может быть пустым
                eventArgs = new UpdatePanelExtenderEventArgs("processGUID can not be empty", DateTime.Now, ExecutionResultState.Error);
            }
            PushResultToClint(eventArgs, elementID);
        }

        private static void PushResultToClint(UpdatePanelExtenderEventArgs e, string elementID)
        {
            //метод, который возвращает результат клинту (клиенту не нужно посылать данные о сериализованном состоянии)
            string err = ((e.ExecutionResultState == ExecutionResultState.Error) || (e.ExecutionResultState == ExecutionResultState.Started)) ? e.ExecutionResult : "";
            UpdatePanelExtenderEventArgs e1 = new UpdatePanelExtenderEventArgs(err, e.StartExecutionTime, e.ExecutionResultState);
            string result = _serializer.Serialize(e1);
            HttpResponse response = HttpContext.Current.Response;
            TextWriter output = response.Output;
            response.ClearContent();
            output.Write(
                "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title></title></head><body><div id='" +
                elementID + "'>");
            output.Write(result);
            output.Write("</div></body></html>");
            response.End();
        }

        public string GetClientStartScript()
        {
            return "$find('" + ClientID + "').ClientStartExecution();";
        }

        private static GetExecutorDelegate ResolveGetExecutorMethod(Object owner, string methodName)
        {
            if (string.IsNullOrEmpty(methodName))
                return null;
            Type type = owner.GetType();
            MethodInfo[] methods = type.GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
            GetExecutorDelegate method = null;
            if (methods != null)
            {
                for (int i = 0; i < methods.Length; i++)
                {
                    if (string.Equals(methodName, methods[i].Name, StringComparison.OrdinalIgnoreCase) && !methods[i].IsGenericMethodDefinition)
                    {
                        try
                        {
                            method = (GetExecutorDelegate)Delegate.CreateDelegate(typeof (GetExecutorDelegate), owner, methods[i], true);
                        }
                        catch (Exception e)
                        {
                            throw new Exception("Method 'GetExecutorMethod' must be compareble with getExecutorDelegate delegate", e);
                        }
                        break;
                    }
                }
            }
            return method;
        }

        private List<T> FindChilds<T>(Control control) where T:class 
        {
            List<T> result = new List<T>();
            for (int i = 0; i < control.Controls.Count; i++)
            {
                T found = control.Controls[i] as T;
                if (found != null)
                {
                    result.Add(found);
                }
            }
            return result;
        }

        private List<T> FindControls<T>(Control firstChiled) where T: class
        {
            List<T> result = new List<T>();
            for (Control control2 = firstChiled.Parent; control2 != null; control2 = control2.Parent)
            {
                result.AddRange(FindChilds<T>(control2));
            }
            return result;
        }

        /// <summary>
        /// Эта основная команда, котора запускает на выполнение длительный процесс
        /// </summary>
        public bool ReserveLongOperation()
        {
            if (_serverMethodCalledWithFinishedState || _isExecutionWasStarted)
                return false;
            _isExecutionWasStarted = true;
            _serverMethodCalledWithFinishedState = false;
            GetExecutorDelegate getExecutorMethod = ResolveGetExecutorMethod(Page, GetExecutorMethod);
            if (getExecutorMethod != null)
            {
                IOperationExecutor executor = getExecutorMethod();
                WorkReuestWrapper wrapper = new WorkReuestWrapper(executor, _synchronizer, Context.Session.SessionID);
                _startedProcessGUID = wrapper.WorkRequestID.ToString();
                _synchronizer.SetOperationState(Context.Session.SessionID, _startedProcessGUID,
                           new UpdatePanelExtenderEventArgs("", DateTime.Now, ExecutionResultState.Undefined));
                _threadPool.PostRequest(wrapper);
                return true;
            }
            return false;
        }

        private UpdatePanelControlTrigger _trigger;
        protected UpdatePanelControlTrigger GetUpdatePanelTrigger()
        {
            if (_trigger != null)
                return _trigger;
            PopulateUpdatePanel();
            UpdatePanelControlTrigger result = null;
            if (_updatePanel != null)
            {
                if (_updatePanel.Triggers.Count > 0)
                {
                    for (int j = 0; j < _updatePanel.Triggers.Count; j++)
                    {
                        if (_updatePanel.Triggers[j] is AsyncPostBackTrigger)
                        {
                            result = (AsyncPostBackTrigger)_updatePanel.Triggers[j];
                            break;
                        }
                        if (_updatePanel.Triggers[j] is PostBackTrigger)
                        {
                            result = (PostBackTrigger)_updatePanel.Triggers[j];
                            break;
                        }
                    }
                }
            }
            _trigger = result;
            return result;
        }

        string _updatePanelRefreshFunction = "";
        private string GetUpdatePanelRefreshFunction()
        {
            if (!string.IsNullOrEmpty(_updatePanelRefreshFunction))
                return _updatePanelRefreshFunction;
            ClientScriptManager clientScript = Page.ClientScript;
            string postBackScript = clientScript.GetPostBackEventReference(_updatePanel,"");
            UpdatePanelControlTrigger trigger = GetUpdatePanelTrigger();
            if (trigger != null)
            {
                Control c = FindControl(trigger.ControlID);
                PostBackOptions postBackOptions = new PostBackOptions(c, "");
                postBackScript = clientScript.GetPostBackEventReference(postBackOptions, false);
            }
            _updatePanelRefreshFunction = postBackScript;
            return postBackScript;
        }

        protected void RegisterStartupScript(string key, string script)
        {
            if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(script))
                return;
            ClientScriptManager clientScript = Page.ClientScript;
            ScriptManager scriptManager = ScriptManager.GetCurrent(Page);
            if ((scriptManager != null) && (scriptManager.SupportsPartialRendering))
            {
                ScriptManager.RegisterStartupScript(_updatePanel, typeof(string), key, script, true);
            }
            else
            {
                if (!clientScript.IsStartupScriptRegistered(key))
                {
                    clientScript.RegisterStartupScript(typeof(string), key, script, true);
                }
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            PopulateUpdatePanel();            
            if (_serverMethodCalledWithFinishedState)
            {

                string script = " $find('" + ClientID + "')._freeUpdatePanel()";
                RegisterStartupScript("hidePopup", script);
                if (_updatePanel != null)
                {
                    if (_updatePanel.UpdateMode == UpdatePanelUpdateMode.Conditional)
                    {
                        _updatePanel.Update();
                    }
                }
            }
            else
            {
                //регистрируем значения для запуска ожидания
                if (_isExecutionWasStarted && Page.IsPostBack)
                {
                    if (_updatePanel != null)
                    {
                        string script = " $find('" + ClientID + "').WaitForExecutionComplete('" + _startedProcessGUID + "'); ";
                        RegisterStartupScript("startWaiting", script);
                        if (_updatePanel.UpdateMode == UpdatePanelUpdateMode.Conditional)
                        {
                            _updatePanel.Update();
                        }
                    }
                }
                if (Page.IsPostBack && (_updatePanel != null))
                {
                    List<UpdatePanelAnimationExtender> animationExtenders =
                        FindControls<UpdatePanelAnimationExtender>(_updatePanel);
                    string q = "";
                    for (int j = 0; j < animationExtenders.Count; j++)
                    {
                        if (animationExtenders[j].TargetControlID == TargetControlID)
                        {
                            q += "$find('" + animationExtenders[j].ClientID + "')._onUpdating.quit();$find('" + animationExtenders[j].ClientID + "')._onUpdated.play();";
                            if (_updatePanel.UpdateMode == UpdatePanelUpdateMode.Conditional)
                            {
                                _updatePanel.Update();
                            }
                        }
                    }
                    RegisterStartupScript(Guid.NewGuid().ToString(), q);
                }
            }
        }

        protected override void Render(HtmlTextWriter writer)
        {
//            if (_httpApplicationEventSuppressor != null)
//            {
//                _httpApplicationEventSuppressor.Resume();
//                _httpApplicationEventSuppressor = null;
//            }
            base.Render(writer);
        }

        protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors(Control targetControl)
        {
            List<ScriptDescriptor> descriptors = null;
            IEnumerable<ScriptDescriptor> baseDescriptors = base.GetScriptDescriptors(targetControl);
            if (baseDescriptors != null)
            {
                descriptors = new List<ScriptDescriptor>(baseDescriptors);
                for (int i = 0; i < descriptors.Count; i++)
                {
                    if (descriptors[i] is ScriptBehaviorDescriptor)
                    {
                        PopulateUpdatePanel();
                        ((ScriptBehaviorDescriptor)descriptors[i]).AddProperty("postBackUrl", Page.Request.RawUrl);
                        ((ScriptBehaviorDescriptor)descriptors[i]).AddProperty("formName", Path.GetFileName(Page.Form.Name));
                        bool trobberSeted = false;
                        if (!string.IsNullOrEmpty(ModalpopupID))
                        {
                            Control control = FindControl(ModalpopupID) as ModalPopupExtender;
                            if (control != null)
                            {
                                trobberSeted = true;
                                ((ScriptBehaviorDescriptor)descriptors[i]).AddComponentProperty("popup", control.ClientID);
                            }
                        }
                        if (!trobberSeted)
                        {
                            if (_modalPopup != null)
                            {
                                ((ScriptBehaviorDescriptor)descriptors[i]).AddComponentProperty("popup", _modalPopup.ClientID);
                            }
                            if (_statusLabel != null)
                            {
                                ((ScriptBehaviorDescriptor)descriptors[i]).AddElementProperty("processingLabel", _statusLabel.ClientID);
                            }
                            if (_cancelButton != null)
                            {
                                ((ScriptBehaviorDescriptor)descriptors[i]).AddElementProperty("cancelButton", _cancelButton.ClientID);
                            }
                        }
                        if (_updatePanel != null)
                        {
                            ((ScriptBehaviorDescriptor)descriptors[i]).AddElementProperty("updatepanel", _updatePanel.ClientID);
                        }
                        List<UpdatePanelAnimationExtender> animationExtenders = FindControls<UpdatePanelAnimationExtender>(_updatePanel);
                        for (int j = 0; j < animationExtenders.Count; j++)
                        {
                            if (animationExtenders[j].TargetControlID == TargetControlID)
                            {
                                ((ScriptBehaviorDescriptor)descriptors[i]).AddComponentProperty("animationextender", animationExtenders[j].ClientID);
                                break;
                            }
                        }
                        string postBackScript = GetUpdatePanelRefreshFunction();
                        ((ScriptBehaviorDescriptor)descriptors[i]).AddProperty("updatePanelRefreshFunction", postBackScript);

                        //регистрируем значения для запуска ожидания (случай, когда ожидание начинается уже при переходе на страницу)
                        if (_isExecutionWasStarted && !Page.IsPostBack && !_serverMethodCalledWithFinishedState)
                        {
                            if (_updatePanel != null)
                            {
                                string script = "{guid:" + _startedProcessGUID + "}";
                                var obj = new
                                {
                                    guid = _startedProcessGUID,
                                    postbackScript = postBackScript
                                };
                                script = _serializer.Serialize(obj);
                                ((ScriptBehaviorDescriptor)descriptors[i]).AddProperty("startUpOperation", script);
                            }
                        }
                        else if (_serverMethodCalledWithFinishedState)
                        {
                            ((ScriptBehaviorDescriptor)descriptors[i]).AddProperty("startUpOperation", "null");
                        }
                        break;
                    }
                }
            }
            return descriptors;
        }

        private void PopulateUpdatePanel()
        {
            if (_updatePanel == null)
            {
                if (!string.IsNullOrEmpty(TargetControlID))
                {
                    UpdatePanel control = FindControl(TargetControlID) as UpdatePanel;
                    if (control != null)
                    {
                        _updatePanel = control;
                    }
                }
            }
        }

        protected void PopulatePopup()
        {
            if (_popupPanel != null)
                return;
            _popupPanel = new Panel();
            Parent.Controls.Add(_popupPanel);
            _popupPanel.ID = "ppp";
            _popupPanel.Style["display"] = "none";
            _popupPanel.Style[HtmlTextWriterStyle.TextAlign] = "center";
            _popupPanel.Style[HtmlTextWriterStyle.VerticalAlign] = "middle";
            _popupPanel.Width = new Unit("200px");
            _popupPanel.Height = new Unit("100px");
            _popupPanel.Style[HtmlTextWriterStyle.BackgroundColor] = "white";
            _popupPanel.Style[HtmlTextWriterStyle.BorderWidth] = "3px";
            _popupPanel.Style[HtmlTextWriterStyle.BorderColor] = "Gray";
            Table contendTable = new Table();
            _popupPanel.Controls.Add(contendTable);
            contendTable.ID = "pct";
            contendTable.Width = new Unit("100%");
            contendTable.Height = new Unit("100%");
            contendTable.BorderWidth = new Unit("1px");
            contendTable.Rows.Add(new TableRow());
            contendTable.Rows[0].Cells.Add(new TableCell());
            contendTable.Rows[0].Cells[0].ColumnSpan = 2;
            contendTable.Rows[0].Cells[0].Text = "Зачекайте";
            contendTable.Rows[0].Cells[0].BackColor = ColorTranslator.FromHtml("#DBE5F1");
            contendTable.Rows.Add(new TableRow());
            contendTable.Rows[1].Height = new Unit("32px");
            contendTable.Rows[1].Cells.Add(new TableCell());
            contendTable.Rows[1].Cells[0].Width = new Unit("18px");
            contendTable.Rows[1].Cells[0].VerticalAlign = VerticalAlign.Top;
            contendTable.Rows[1].Cells.Add(new TableCell());
            contendTable.Rows[1].Cells[1].VerticalAlign = VerticalAlign.Top;

            _customTrobber = new Image();
            contendTable.Rows[1].Cells[0].Controls.Add(_customTrobber);
            _customTrobber.ID = "ppt";
            _customTrobber.ImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(UpdatePanelExtender), "AjaxControlToolkit.LongOperations.loading2.gif");

            _statusLabel = new Label();
            contendTable.Rows[1].Cells[1].Controls.Add(_statusLabel);
            _statusLabel.ID = "ppsl";

            contendTable.Rows.Add(new TableRow());
            contendTable.Rows[2].Cells.Add(new TableCell());
            contendTable.Rows[2].Cells[0].ColumnSpan = 2;
            contendTable.Rows[2].Cells[0].HorizontalAlign = HorizontalAlign.Right;

            _cancelButton = new Button();
            contendTable.Rows[2].Cells[0].Controls.Add(_cancelButton);
            _cancelButton.ID = "ceb";
            _cancelButton.Text = "Відмінити виконання";

            _modalPopup = new ModalPopupExtender();
            Controls.Add(_modalPopup);
            _modalPopup.ID = "mpp";
            _modalPopup.TargetControlID = _popupPanel.ID;
            _modalPopup.PopupControlID = _popupPanel.ID;
            _modalPopup.RepositionMode = ModalPopupRepositionMode.RepositionOnWindowResizeAndScroll;
            _modalPopup.DropShadow = false;
            _modalPopup.CancelControlID = _cancelButton.ID;
            _modalPopup.OnCancelScript = "$find('"+ClientID+"').CanelExecution();";
            _modalPopup.BackgroundCssClass = "ajax_UpdatePanelExtender_modalpopup_bg";

//            RoundedCornersExtender roundedCornersExtender = new RoundedCornersExtender();
//            roundedCornersExtender.BorderColor = ColorTranslator.FromHtml("#BC4700");
//            roundedCornersExtender.TargetControlID = _popupPanel.ID;
//            roundedCornersExtender.Corners = BoxCorners.All;
//            roundedCornersExtender.Radius = 4;
//            Controls.Add(roundedCornersExtender);
        }
    }
}
